package com.ciei.dpagm.util;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import sun.misc.BASE64Decoder;
import sun.net.www.protocol.file.FileURLConnection;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author wy
 *
 */
public class FileUtils {

	private static final Logger log = Logger.getLogger(FileUtils.class);

	private static final String fileSeparator = "/";

	/**
	 * 把一个文件中的内容读取成一个String字符串
	 * @return
	 */
	public static String getStr(String filePath){
		File file = new File(filePath);
		return getStr(file);
	}

	/**
	 * 把一个文件中的内容读取成一个String字符串
	 * @return
	 */
	public static String getStr(File file) {
		try(FileReader fileReader = new FileReader(file);
			BufferedReader br = new BufferedReader(fileReader)){
			// 读取文件内容
			StringBuilder sb = new StringBuilder();
			String line;
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
			return sb.toString();
		}catch (Exception e){
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 根据模板路径把一个文件中的内容读取成一个String字符串
	 * @return
	 */
	public static String getStrByTemplatePath(String templatePath) {
		ClassPathResource template = new ClassPathResource(templatePath);
		File file = null;
		try {
			InputStream inputStream = template.getInputStream();
			file = File.createTempFile("doc", ".tmp");
			file.deleteOnExit();
			FileOutputStream out = new FileOutputStream(file);
			IOUtils.copy(inputStream, out);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return getStr(file);
	}

	public static void checkDirs(String path) {
		File file = new File(path);
		if (!file.exists()) {
			file.mkdirs();
		}
	}

	/**
	 * 检查文件是否存在
	 * @param path
	 * @return
	 */
	public static Boolean checkFileExistence(String path) {
		File file = new File(path);
		return file.exists();
	}

	/**
	 * 复制文件夹
	 * @param sourcePath    原文件夹绝对路径
	 * @param newPath       新文件夹绝对路径
	 * @return              是否复制成功
	 */
	public static boolean copyDirectory(String sourcePath, String newPath) {
		File file = new File(sourcePath);
		String[] fileNames = file.list();
		if (fileNames == null) {
			return false;
		}

		if (!(new File(newPath)).exists()) {
			(new File(newPath)).mkdir();
		}

		boolean isSuccess = true;
		for (String fileName : fileNames) {
			String sourceFilePath = sourcePath + fileSeparator + fileName;
			File sourceFile = new File(sourceFilePath);
			if (sourceFile.isDirectory()) {
				isSuccess &= copyDirectory(sourceFilePath, newPath + fileSeparator + fileName);
			}
			if (sourceFile.isFile()) {
				isSuccess &= copyFile(sourceFilePath, newPath + fileSeparator + fileName);
			}
		}
		return isSuccess;
	}

	/**
	 * 复制文件
	 * @param sourcePath    原文件绝对路径
	 * @param newPath       新文件绝对路径
	 * @return              是否复制成功
	 */
	public static boolean copyFile(String sourcePath, String newPath) {
		File sourceFile = new File(sourcePath);
		File targetFile = new File(newPath);
		try (FileInputStream in = new FileInputStream(sourceFile);
			 FileOutputStream out = new FileOutputStream(targetFile)) {
			byte[] buffer = new byte[2 * 1024 * 1024];
			int readByte;
			while((readByte = in.read(buffer)) != -1) {
				out.write(buffer, 0, readByte);
			}
			return true;
		} catch (IOException e) {
			e.printStackTrace();
		}

		return false;
	}

	/**
	 * 删除文件
	 * <p>如果删除对象为文件夹，则递归删除其目录下的所有文件和文件夹</p>
	 * @param filePath      将要删除的文件路径
	 * @return          是否删除成功
	 */
	public static boolean deleteFile(String filePath) {
		File file = new File(filePath);
		if (!file.exists()){
			return false;
		}
		return deleteFile(file);
	}

	/**
	 * 删除文件
	 * <p>如果删除对象为文件夹，则递归删除其目录下的所有文件和文件夹</p>
	 * @param file      将要删除的文件
	 * @return          是否删除成功
	 */
	public static boolean deleteFile(File file) {
		if (file.isDirectory()) {
			String[] childrenFileNames = file.list();
			if (childrenFileNames != null) {
				// 递归删除目录中的文件/文件夹
				for (String childFileName : childrenFileNames) {
					if (!deleteFile(new File(file, childFileName))) {
						return false;
					}
				}
			}
		}
		// 为文件或空目录，可以删除
		return file.delete();
	}

	/**
	 * 从File或Jar复制资源文件
	 * @param sourceFolderPath
	 * @param targetFolderPath
	 * @param clazz
	 * @throws IOException
	 */
	public static void copyResourcesFromFileOrJar(String sourceFolderPath,String targetFolderPath ,Class clazz) throws IOException {
		URL url = clazz.getResource(sourceFolderPath);
		URLConnection urlConnection = url.openConnection();
		if (urlConnection instanceof FileURLConnection) {
			copyDirectory(url.getPath(),targetFolderPath);
		} else if (urlConnection instanceof JarURLConnection) {
			copyJarResources((JarURLConnection) urlConnection,sourceFolderPath,targetFolderPath,clazz);
		}
	}

	/**
	 * 复制jar资源文件
	 *
	 * @param jarURLConnection
	 * @throws IOException
	 */
	private static void copyJarResources(JarURLConnection jarURLConnection,String sourceFolderPath,String targetFolderPath,Class clazz) throws IOException {
		JarFile jarFile = jarURLConnection.getJarFile();
		Enumeration<JarEntry> entrys = jarFile.entries();
		while (entrys.hasMoreElements()) {
			JarEntry entry = entrys.nextElement();
			if (entry.getName().startsWith(jarURLConnection.getEntryName()) && !entry.getName().endsWith("/")) {
				loadRecourseFromJar("/" + entry.getName(),sourceFolderPath,targetFolderPath,clazz);
			}
		}
		jarFile.close();
	}

	/**
	 * 执行复制
	 * @param path
	 * @param sourceFolderPath
	 * @param targetFolderPath
	 * @param clazz
	 * @throws IOException
	 */
	public static void loadRecourseFromJar(String path,String sourceFolderPath,String targetFolderPath,Class clazz) throws IOException {
		if (!path.startsWith("/")) {
			throw new IllegalArgumentException("The path has to be absolute (start with '/').");
		}
		if (path.endsWith("/")) {
			throw new IllegalArgumentException("The path has to be absolute (cat not end with '/').");
		}
		String filePath = path.replace(sourceFolderPath,"");
		int index = filePath.lastIndexOf('/');
		String filename = filePath.substring(index + 1);
		String folderPath = targetFolderPath + filePath.substring(0, index + 1);
		// If the folder does not exist yet, it will be created. If the folder
		// exists already, it will be ignored
		File dir = new File(folderPath);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		// If the file does not exist yet, it will be created. If the file
		// exists already, it will be ignored
		filename = folderPath + filename;
		File file = new File(filename);

		if (!file.exists() && !file.createNewFile()) {
			log.error("create file :{} failed .fileName:"+ filename);
			return;
		}
		// Prepare buffer for data copying
		byte[] buffer = new byte[1024];
		int readBytes;
		// Open and check input stream
		URL url = clazz.getResource(path);
		URLConnection urlConnection = url.openConnection();
		InputStream is = urlConnection.getInputStream();
		if (is == null) {
			throw new FileNotFoundException("File " + path + " was not found inside JAR.");
		}
		OutputStream os = new FileOutputStream(file);
		try {
			while ((readBytes = is.read(buffer)) != -1) {
				os.write(buffer, 0, readBytes);
			}
		} finally {
			os.close();
			is.close();
		}
	}

	/**
	 * 文件转base64
	 * @param filePath
	 * @return
	 */
	public static String fileToBase64(String filePath) {
		File file = new File(filePath);
		try (FileInputStream fileInputStream = new FileInputStream(file)) {
			byte[] fileBytes = new byte[(int) file.length()];
			fileInputStream.read(fileBytes);
			return Base64.getEncoder().encodeToString(fileBytes);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * base64字符串转文件
	 * @param base64Str
	 * @param writeFile
	 * @return
	 */
	public static void base64ToFile(String base64Str,File writeFile) {
		byte[] decodedBytes = Base64.getDecoder().decode(base64Str);
		try (FileOutputStream fos = new FileOutputStream(writeFile)){
			fos.write(decodedBytes);
		} catch (Exception e) {
			log.error("base64ToFile error:",e);
		}
	}

	/**
	 * 获取随机文件名称
	 * @param originalFilename
	 * @return
	 */
	public static String getRandomFileName(String originalFilename){
		//随机生成文件名，避免重复
		UUID uuid  =  UUID.randomUUID();
		String name = uuid.toString();
		//替换掉中间的那个横杠
		name = name.replace("-", "");
		//获取文件的后缀名 如.jpg
		int lastIndexOf = originalFilename.lastIndexOf(".");
		String suffix = lastIndexOf == -1 ? "" : originalFilename.substring(lastIndexOf);
		String fileName = name + suffix;
		return fileName;
	}

	/**
	 * 获取文件md5值
	 * @param filePath
	 * @return
	 */
	public static String getFileMd5(String filePath){
		String fileMd5 = "";
		try(FileInputStream fis = new FileInputStream(filePath)) {
			fileMd5 = DigestUtils.md5Hex(fis);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return fileMd5.toUpperCase();
	}

	/**
	 * 通过文件输入流生成base64文件
	 * @param inputStream 输入流
	 * @return String
	 */
	public static String createBase64ByInputStream(InputStream inputStream){
		Base64.Encoder encoder = Base64.getEncoder();
		byte[] fileBytes = null;
		ByteArrayOutputStream byteArrayOutputStream = null;
		try {
			byteArrayOutputStream = new ByteArrayOutputStream();
			byte[] buff = new byte[100];
			int rc = 0;
			while ((rc = inputStream.read(buff, 0, 100)) > 0) {
				byteArrayOutputStream.write(buff, 0, rc);
			}
			fileBytes = byteArrayOutputStream.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (byteArrayOutputStream != null) {
				try {
					byteArrayOutputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return encoder.encodeToString(fileBytes);
	}

	/**
	 * 将base64转换为multipartFile
	 * @param base64 base64字符串
	 * @param fileName 文件名
	 * @return
	 */
	public static MultipartFile base64ToMultipart(String base64,String fileName) {
		try{
			byte[] bytes = Base64.getDecoder().decode(base64);
			return new Base64DecodedMultipartFile(bytes, fileName);
		}catch (Exception e){
			log.error("base64ToMultipart error:",e);
			log.debug("error base64:"+base64);
			return null;
		}
	}

	/**
	 * 从request中获取Excel文件
	 * @param request 请求
	 */
	public static MultipartFile getMultipartFileFromRequest(HttpServletRequest request){

		String contentType = request.getContentType();
		MultipartFile resultmultipartFile = null;
		//获取请求参数
		if(StringUtils.isNotBlank(contentType) && contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)){
			JSONObject requestJsonObject = WebUtils.getRequestJsonObject(request);
			//获取fileName
			String fileName = requestJsonObject.getString("fileName");
			// 获取 fileContent 参数值
			String fileContent = requestJsonObject.getString("fileContent");
			if (StringUtils.isNotBlank(fileName)) {
				// 将 base64 转为 multipartFile
				resultmultipartFile = FileUtils.base64ToMultipart(fileContent,fileName);
			}
			return resultmultipartFile;
		}else {
			// 创建一个通用的多部分解析器
			CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession()
					.getServletContext());
			// 判断 request 是否有文件上传,即多部分请求
			if (multipartResolver.isMultipart(request)) {
				// 转换成多部分request
				MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
				// 取得request中的所有文件名
				Iterator<String> iter = multiRequest.getFileNames();
				List<MultipartFile> multipartFileList = new ArrayList<>();
				while (iter.hasNext()) {
					// 取得上传文件
					MultipartFile multipartFile = multiRequest.getFile(iter.next());
					multipartFileList.add(multipartFile);
				}
				if (!multipartFileList.isEmpty()) {
					resultmultipartFile = multipartFileList.get(0);
				}
				return resultmultipartFile;

			}
		}
		return null;
	}


}
